#!/bin/bash
# This script load zfs kernel module for any archiso.
# github.com/eoli3n
# Thanks to CalimeroTeknik on #archlinux-fr, FFY00 on #archlinux-projects, JohnDoe2 on #regex

exec &> >(tee "debug.log")

### Vars

verbose=0

### Functions

usage () {
    cat << EOF
Usage: ${0##*/} [-v]

    -v    increase verbosity
    -h    show this usage
EOF
}

print () {
    echo -e "\n\033[1m> $1\033[0m"
}

get_running_kernel_version () {
# Returns running kernel version

    # Get running kernel version
    kernel_version=$(uname -r)
    
    print "Current kernel version is $kernel_version"
}

init_archzfs () {
    if pacman -Sl archzfs >&3; then
        print "archzfs repo was already added"
        return 0
    fi
    print "Add archzfs repo"
    
    # Disable Sig check
    pacman -Syy archlinux-keyring --noconfirm >&3 || return 1
    pacman-key --populate archlinux >&3 || return 1
    pacman-key --recv-keys F75D9D76 >&3 || return 1
    pacman-key --lsign-key F75D9D76 >&3 || return 1
    cat >> /etc/pacman.conf <<"EOF"
[archzfs]
Server = http://archzfs.com/archzfs/x86_64
Server = http://mirror.sum7.eu/archlinux/archzfs/archzfs/x86_64
Server = https://mirror.biocrafting.net/archlinux/archzfs/archzfs/x86_64
EOF
    pacman -Sy >&3 || return 1
    return 0
}

init_archlinux_archive () {
# $1 is date formated as 'YYYY/MM/DD'
# Returns 1 if repo does not exists

    # Archlinux Archive workaround for 2022/02/01
    if [[ "$1" == "2022/02/01" ]]
    then
        version="2022/02/02"
    else
        version="$1"
    fi

    # Set repo
    repo="https://archive.archlinux.org/repos/$version/"

    # If repo exists, set it
    if curl -s "$repo" >&3
    then
        echo "Server=$repo\$repo/os/\$arch" > /etc/pacman.d/mirrorlist
    else
        print "Repository $repo is not reachable or doesn't exist."
        return 1
    fi

    return 0
}

search_package () {
# $1 is package name to search
# $2 is version to match

    # Set regex to match package
    local regex='href="\K(?![^"]*\.sig)'"$1"'-(?=\d)[^"]*'"$2"'[^"]*x86_64[^"]*'
    # href="               # match href="
    # \K                   # don't return anything matched prior to this point
    # (?![^"]*\.sig)       # remove .sig matches
    # '"$1"'-(?=\d)        # find me '$package-' escaped by shell and ensure that after "-" is a digit
    # [^"]*                # match anything between '"'
    # '"$2"'               # match version escaped by shell
    # [^"]*                # match anything between '"'
    # x86_64               # now match architecture
    # [^"]*                # match anything between '"'
    
    # Set archzfs URLs list
    local urls="http://archzfs.com/archzfs/x86_64/ http://archzfs.com/archive_archzfs/"
    
    # Loop search
    for url in $urls
    do
    
        print "Searching $1 on $url..."
    
        # Query url and try to match package
        local package
        package=$(curl -s "$url" | grep -Po "$regex" | tail -n 1)
    
        # If a package is found
        if [[ -n $package ]]
        then
    
            print "Package \"$package\" found"
    
            # Build package url
            package_url="$url$package"
            return 0
        fi
    done

    # If no package found
    return 1
}

download_package () {
# $1 is package url to download in tmp

    print "Download to $package_file ..."

    local filename="${1##*/}"

    # Download package in tmp
    cd /tmp || return 1
    curl -sO "$1" || return 1
    cd - || return 1

    # Set out file
    package_file="/tmp/$filename"

    return 0
}

zfslinux_install () {
    # Search kernel package
    # https://github.com/archzfs/archzfs/issues/337#issuecomment-624312576
    get_running_kernel_version
    kernel_version_fixed="${kernel_version//-/\.}"
    
    # Search zfs-linux package matching running kernel version
    if search_package "zfs-linux" "$kernel_version_fixed"
    then
    
        zfs_linux_url="$package_url"
    
        # Download package
        download_package "$zfs_linux_url" || exit 1
        zfs_linux_package="$package_file"
    
        print "Extracting zfs-utils version from zfs-linux PKGINFO"
    
        # Extract zfs-utils version from zfs-linux PKGINFO
        zfs_utils_version=$(bsdtar -qxO -f "$zfs_linux_package" .PKGINFO | grep -Po 'depend = zfs-utils=\K.*')
    
        # Search zfs-utils package matching zfs-linux package dependency
        if search_package "zfs-utils" "$zfs_utils_version"
        then
            zfs_utils_url="$package_url"
    
            print "Installing zfs-utils and zfs-linux"
    
            # Install packages
            if pacman -U "$zfs_utils_url" --noconfirm >&3 && pacman -U "$zfs_linux_package" --noconfirm >&3
            then
                zfs=1
            else
                return 1
            fi
        fi
    fi

    return 0
}

dkms_install () {
# Init everything to be able to install zfs-dkms
    print "No zfs-linux package was found for current running kernel, fallback on DKMS method"

    print "Init Archlinux Archive repository"
    archiso_version=$(sed 's-\.-/-g' /version)
    init_archlinux_archive "$archiso_version" || return 1

    print "Download Archlinux Archives package lists and upgrade"
    pacman -Syyuu --noconfirm >&3 || return 1

    print "Install base-devel"
    pacman -S --noconfirm base-devel linux-headers git >&3 || return 1

    print "Install zfs-dkms"
    
    # Install package
    if pacman -S zfs-dkms --noconfirm >&3
    then
        zfs=1
    else
        return 1
    fi
    return 0
}

### Getopts

while getopts "vh" option; do
    case "${option}" in
        v)
            verbose=$((verbose + 1))
            ;;
        h)
            usage
            exit 0
            ;;
        *)
            usage
            exit 0
            ;;
    esac
done
shift $((OPTIND-1))

### Verbose mode

if [[ "$verbose" -gt 0 ]]
then
    exec 3>&1
else
    exec 3>/dev/null
fi

### Main

# Test if archiso is running

if ! grep 'arch.*iso' /proc/cmdline >&3
then
    print "You are not running archiso, exiting."
    exit 1
fi

print "Increase cowspace to half of RAM"

mount -o remount,size=50% /run/archiso/cowspace >&3

# Init archzfs repository
init_archzfs || exit 1

# Install zfs-linux if found else fallback on dkms_install
zfslinux_install || dkms_install || exit 1

# Load kernel module
if [[ "$zfs" == "1" ]]
then

    modprobe zfs && echo -e "\n\e[32mZFS is ready\n"

else
    print "No ZFS module found"
fi
